#lang racket
;; a RawWed is a [Listof [Listof Nodes]]
;; Each inner list represents a level

;; a Web is a RawWeb with all nodes linked

(provide node-id node-phase node-depth node-links node-parent set-node-parent! LEVELS PHASES make-web node-count)

#|
A web is organize into levels(depth) and phases

A web can be visually thought of as layered consentric circles, like the rings 
of a tree where the depth is how far you are up the tree and phases are which 
ring of the tree you are on, 0 being the center.
|#



;; Symbol Number Number [listof Nodes] Any
(struct node (id phase depth links [parent #:auto]) #:auto-value null #:mutable)

(define LEVELS 10)
(define PHASES 10)

;; Web -> Number
;; counts the number of nodes in the web
(define (node-count web)
  (for*/fold ([count 0])
    ([l web]
     [n l])
    (add1 count)))

;; -> Web
;; makes a web with links in place
(define (make-web)
  (link-web! (make-unlinked-web)))

;; RawWeb -> RawWeb
;; links the webs phases together
(define (link-web! raw)
  (for* ([level raw]
         [node level])
    (link-node! node raw))
  raw)

;; Node RawWeb -> (void)
;; links that node to the web
(define (link-node! n raw)
  (for ([r (get-link-pool (node-phase n) (node-depth n) raw)]
        #:when (not (eq? n r)))
    (when (zero? (random 2))
      (node-link! n r)))
  (when (empty? (node-links n))
    (link-node! n raw)))

;; Number Number RawWeb -> [Listof Nodes]
;; gets the nodes on that level one phase in or out, and on that phase one level up or down
(define (get-link-pool p d raw)
  (append (if (> d 0)
              (filter (λ (n) (and (= p (node-phase n))))
                      (list-ref raw (sub1 d)))
              '())
          (filter (λ (n) (<= (sub1 p) (node-phase n) (add1 p)))
                  (list-ref raw d))
          (if (< d (sub1 LEVELS))
              (filter (λ (n) (= p (node-phase n)))
                      (list-ref raw (add1 d)))
              '())))

;; -> RawWeb
;; builds an unlinked web
(define (make-unlinked-web)
  (define (add-level c prev)
    (cond [(= c PHASES) prev]
          [(add-level (add1 c) (add-unlinked-phase c prev))]))
  (add-level 1 (make-phase-0)))

;; Number RawWeb -> RawWeb
;; adds that phase to the web without linking the levels
(define (add-unlinked-phase n incomplete)
  (define phase (make-unlinked-phase n))
  (for/list ([l1 phase]
             [l2 incomplete])
    (append l1 l2)))

;; Number -> [Listof Nodes]
;; builds unlinked nodes in that phase
(define (make-unlinked-phase n)
  (for/list ([l (in-range LEVELS)])
    (make-phase/level n l)))

;; Number Number -> [Listof Nodes]
;; builds that phase at that level
(define (make-phase/level n l)
  (for/list ([id (in-range (* n (add1 l)))])
    (node (make-node-id n l) n l '())))

;; -> RawWeb
;; makes phase0 and links its levels
(define (make-phase-0)
  (define raw-phase 
    (for/list ([l (in-range LEVELS)])
      (node (make-node-id 0 l) 0 l '())))
  (define (add-linkage raw)
    (unless (empty? (rest raw))
      (node-link! (first raw)
                  (second raw))
      (add-linkage (rest raw))))
  (add-linkage raw-phase)
  (map list raw-phase))

;; Node Node -> (void)
;; links the two nodes together
(define (node-link! n1 n2)
  (node-add-con! n1 n2)
  (node-add-con! n2 n1))

;; Node Node -> (void)
;; adds the scnd node to frst links
(define (node-add-con! n1 n2)
  (set-node-links! n1 (cons n2 (node-links n1))))

;; Number Number -> symbol
;; makes an id for a node given phase and depth
(define (make-node-id p d)
  (string->symbol (string-append "phase" 
                                 (number->string p)
                                 "level"
                                 (number->string d))))
